﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using ProjectBase.Core;

using $prjsn$.Common;
using $prjsn$.Common.Validation;

namespace $safeprojectname$
{
    public abstract class ValidatorBase<TItem> : IValidator<TItem>
        where TItem : class
    {
        #region members

        #endregion members

        #region Init
        protected abstract void Init(RuleCollection<TItem> rules);
        #endregion Init

        #region Validate
        public virtual bool Validate(TItem item, ValidateOn validateOn, IPublishingReporter reporter = null)
        {
            // no item, no validation
            if (item.IsNull())
            {
                if (reporter.Is())
                {
                    PublishingManager.Publish(reporter, Severity.Error
                        , Str.Messages.NullInsteadOfEntityProvided1, typeof(TItem).Name);
                }
                return false;
            }

            var result = true;

            foreach (var rule in Rules.Select(validateOn, ApplyOn.Server))
            {
                ValidationMessage message;
                if (rule.Validate(item, out message).Equals(ValidationResult.IsValid))
                {
                    continue; // OK
                }

                // publish message
                if (reporter.Is())
                {
                    PublishingManager.Publish(reporter, message.Severity, message.Template, message.Arguments);

                    if (message.InnerMessages.IsNotEmpty())
                    {
                        foreach (var innerMessage in message.InnerMessages)
                        {
                            PublishingManager.Publish(reporter, innerMessage.Severity, innerMessage.Template, innerMessage.Arguments);
                        }
                    }
                }

                result = false; // Error
                if (rule.BreakOnError)
                {
                    break;
                }
            }
            return result;
        }
        #endregion Validate

        #region Client
        /// <summary>
        /// Returns the (JS) string representation of the UI evaluable validation rules.
        /// (Example: validator.rules["LastName"] = { required: true, minlength: 1,maxlength: 50};)
        /// </summary>
        /// <param name="validationType">The switch which rule type to use. (e.g. "Add", "Update")</param>
        /// <returns>(JS) validation rules</returns>
        public virtual string ValidationRulesToString(string validationType, string prefix = null)
        {
            ValidateOn? validateOn = null;

            if (validationType.IsEqual(Str.Actions.Find)
                || validationType.IsEqual(Str.Actions.Search))
            {
                validateOn = ValidateOn.Find;
            }
            else if (validationType.IsEqual(Str.Actions.New)
                || validationType.IsEqual(Str.Actions.NoView.Add))
            {
                validateOn = ValidateOn.Add;
            }
            else if (validationType.IsEqual(Str.Actions.Edit)
                || validationType.IsEqual(Str.Actions.NoView.Update))
            {
                validateOn = ValidateOn.Update;
            }

            if (validateOn.IsNull())
            {
                return string.Empty;
            }

            var sb = new StringBuilder();
            foreach (var rule in Rules.Select(validateOn.Value, ApplyOn.Client))
            {
                sb.AppendLine(rule.GetClientRule(prefix));
            }
            return sb.ToString();
        }
        #endregion Client

        #region properties
        protected static RuleCollection<TItem> RulesCollection { get; set; }
        protected virtual RuleCollection<TItem> Rules
        {
            get
            {
                var rules = RulesCollection;
                if (rules.IsNull())
                {
                    rules = new RuleCollection<TItem>();
                    Init(rules);
                    RulesCollection = rules;
                }
                return rules;
            }
        }
        #endregion properties
    }
}